﻿Imports System.IO

Public MustInherit Class FitDocument

    Private Shared mDocumentTypeMap As Dictionary(Of Byte, Type)

    Shared Sub New()
        mDocumentTypeMap = New Dictionary(Of Byte, Type)

        mDocumentTypeMap.Add(4, GetType(ActivityDocument))
    End Sub

    Public Shared Function Parse(stream As IO.Stream) As FitDocument
        Dim reader As New BinaryReader(stream)
        'Read  header
        Dim HeaderLength As Byte = reader.ReadByte 'First byte is the length of the header (spec says this should always be 12, but I have a FIT file that says 14)
        Dim ProtocolVersion As Integer
        Dim ProfileVersion As Integer
        Dim DataLength As UInteger
        Dim FitMarker As Byte()
        ProtocolVersion = reader.ReadByte
        ProfileVersion = reader.ReadUInt16
        DataLength = reader.ReadUInt32
        FitMarker = reader.ReadBytes(4)

        stream.Seek(HeaderLength, SeekOrigin.Begin) 'position reader at start of data area

        Dim Document As FitDocument = Nothing 'The document type is unknown until we read the FileID Message. FIT Spec says that FileID must be the first message in a .fit file.
        Dim MessageMap As New MessageMap

        Do
            Dim Message As MessageBase = MessageBase.Parse(reader, MessageMap)

            'Check to see if this is a FileId message, the spec says this must be teh first type of message in a FIT file.
            If TypeOf Message Is FileIdMessage Then
                If Document IsNot Nothing Then
                    Throw New InvalidOperationException("File Type was specified more than once.")
                End If

                Dim FileIdMessage As FileIdMessage = CType(Message, FitLib.FileIdMessage)
                If mDocumentTypeMap.ContainsKey(FileIdMessage.Type) Then
                    Document = CType(Activator.CreateInstance(mDocumentTypeMap(FileIdMessage.Type)), FitDocument)
                Else
                    Throw New InvalidOperationException("File Type is not supported")
                End If
            End If

            If Document IsNot Nothing Then
                Document.ProcessMessageInternal(Message)
            End If
        Loop Until stream.Position >= stream.Length - 3 'last 2 bytes are a CRC

        Return Document
    End Function

    Protected mFileIdMessage As FileIdMessage
    Public ReadOnly Property FileIdMessage As FileIdMessage
        Get
            Return mFileIdMessage
        End Get
    End Property

    Protected MustOverride Sub ProcessMessage(message As MessageBase)

    Protected Overridable Sub ProcessMessageInternal(message As MessageBase)
        'code common to all document types should be processed here, before any document specific code.
        If TypeOf message Is FileIdMessage Then
            mFileIdMessage = CType(message, FitLib.FileIdMessage)
        End If

        ProcessMessage(message)
    End Sub
End Class
